﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using AgilePlannerDT.Utils;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using System.Timers;
using AgilePlannerDT.Data;
using AgilePlannerDT.Display.Events;
using AgilePlannerDT.Application;
using System.Windows.Media.Effects;
//for automation
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using AgilePlannerDT.Window;

namespace AgilePlannerDT.Display.Impl
{
    public class DisplayObjectImpl : UserControl, DisplayObject
    {
        #region Constants

        #endregion

        #region Attributes

        #region Flags

        protected bool isTossed = false;
        protected bool isDragged = false;
        protected bool isResized = false;
        protected bool isTranslated = false;
        protected bool isTossingEnabled = false;
        protected bool isRNTEnabled = false;
        protected bool isDeleted = false;
        protected bool isPinned = false;
        protected bool isHighlighted = false;
        protected bool isActive = false;
        protected bool isSelected = false;

        #endregion

        #region Transformations

        protected TransformGroup group;
        protected RotateTransform rotate;
        protected TranslateTransform trans;
        protected SkewTransform skew;
        protected ScaleTransform scale;

        #endregion

        #region Animations

        protected Storyboard animationStoryBoard, fadingStoryBoard;
        protected DoubleAnimationUsingKeyFrames translateXAnimation, translateYAnimation, rotateAnimation;

        #endregion

        #region Tossing

        protected Point tossingVector, initVector;
        protected double initAngle;
        protected DateTime startTime, endTime;

        #endregion

        #region Events

        public delegate void IndexCardDroppedEventHandler(DisplayObject sender, IndexCardDropEventArgs e);
        public event IndexCardDroppedEventHandler OnIndexCardDropped;

        public delegate void FadeOutCompletedEventHandler(DisplayObject sender);
        public event FadeOutCompletedEventHandler OnFadeOutCompleted;

        #endregion

        protected Point center, previous, previousRelative;

        protected List<Point> recentPoints;

        protected List<DateTime> recentTime;

        protected AgilePlannerApplication app;

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor, neccessary for XAML compliance <see cref="DisplayObjectImpl"/> class.
        /// </summary>
        public DisplayObjectImpl()
        {
        }

        public DisplayObjectImpl(AgilePlannerApplication mainWindow)
        {
            this.app = mainWindow;
            recentPoints = new List<Point>();
            recentTime = new List<DateTime>();

            Loaded += new RoutedEventHandler(DisplayObjectImpl_Loaded);
        }

        #endregion

        #region Properties

        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                if (isSelected == value)
                {
                    return;
                }
                isSelected = value;
            }
        }

        public bool IsPinned
        {
            get { return isPinned; }
            set
            {
                if (isPinned == value)
                {
                    return;
                }
                isPinned = value;
            }
        }

        public bool IsTossed
        {
            get { return isTossed; }
            set
            {
                if (isTossed == value)
                {
                    return;
                }
                isTossed = value;
            }
        }

        public bool IsDragged
        {
            get { return isDragged; }
            set
            {
                if (isDragged == value)
                {
                    return;
                }
                isDragged = value;
            }
        }

        public bool IsResized
        {
            get { return isResized; }
            set
            {
                if (isResized == value)
                {
                    return;
                }
                isResized = value;
            }
        }

        public bool IsTranslated
        {
            get { return isTranslated; }
            set
            {
                if (isTranslated == value)
                {
                    return;
                }
                isTranslated = value;
            }
        }

        public bool IsTossingEnabled
        {
            get { return isTossingEnabled; }
        }

        public bool IsRNTEnabled
        {
            get { return isRNTEnabled; }
        }

        public bool IsActive
        {
            get { return this.isActive; }
            set { this.isActive = value; }
        }

        #endregion

        #region DisplayObject Members

        #region Properties

        public Point CenterAbsolute
        {
            get { return RelativeToAbsoluteCoordinates(center); }
        }

        /// <summary>
        /// Gets or sets the ID. Needs to be overriden in subclasses!
        /// </summary>
        /// <value>The ID.</value>
        public virtual long ID
        {
            get { return 0; }
            set { }
        }

        public double LocationX
        {
            get { return trans.X; }
        }

        public double LocationY
        {
            get { return trans.Y; }
        }

        /// <summary>
        /// Gets or sets the parent ID. Needs to be overriden in subclasses!
        /// </summary>
        /// <value>The parent ID.</value>
        public virtual long ParentID
        {
            get { return 0; }
            set { }
        }

        public double RotationAngle
        {
            get { return rotate.Angle; }
        }

        #endregion

        #region Functions

        public void AnimatedMoveTo(double targetX, double targetY)
        {
            AnimatedMoveTo(targetX, targetY, rotate.Angle);
        }

        public virtual void AnimatedMoveTo(double targetX, double targetY, double targetRotationAngle)
        {
            //ensures that remote moves are not interfering while you are
            //moving the card locally. Solves a problem with the round-trip-time of
            //messages from the webservice that messed-up the movement
            if (!IsActive)
            {
                translateXAnimation.KeyFrames.Clear();
                translateYAnimation.KeyFrames.Clear();
                rotateAnimation.KeyFrames.Clear();

                double difX = trans.X - targetX;
                double difY = trans.Y - targetY;
                double difRot = rotate.Angle - targetRotationAngle;

                //check what is closer target or (target - 360)
                if ((int)difX != 0)
                {
                    translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(trans.X));
                    translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(targetX));
                    translateXAnimation.Duration = CardConstants.REORDER_ANIMATION_DURATION;
                }

                if ((int)difY != 0)
                {
                    translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(trans.Y));
                    translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(targetY));
                    translateYAnimation.Duration = CardConstants.REORDER_ANIMATION_DURATION;
                }

                if ((int)difRot != 0)
                {
                    rotateAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(rotate.Angle));
                    rotateAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(targetRotationAngle));
                    rotateAnimation.Duration = CardConstants.REORDER_ANIMATION_DURATION;
                }

                if (!(((int)difRot == 0) && ((int)difX == 0) && ((int)difY == 0)))
                    animationStoryBoard.Begin(this, true);
               //since the animation is not started, we must make sure the triggered Drop event set back to true
                else if (this is StoryCardDisplayObjectImpl)
                {
                    ((StoryCardDisplayObjectImpl)this).setTriggerDropEventAfterAnimiationComplete(true);
                    Console.WriteLine("this is " + this.ID + ", " + "in animation move to");
                }
            }
        }

        public void AnimatedRotateTo(double targetRotationAngle)
        {
            AnimatedMoveTo(trans.X, trans.Y, targetRotationAngle);
        }

        public void BringToFront()
        {
            app.BringToFront(this);
        }

        public bool Contains(Point p)
        {
            return Util.IsPointInsidePolygon(p, GetCorners());
        }

        public void FadeIn()
        {
            Fading(0, Properties.Settings.Default.Scale, false);

        }

        public void FadeOut()
        {
            isDeleted = true;
            Fading(Properties.Settings.Default.Scale, 0, false);

        }

        public Point[] GetCorners()
        {
            return new Point[4] {
                RelativeToAbsoluteCoordinates(0,0),
                RelativeToAbsoluteCoordinates(0, Math.Max(ActualHeight, Height)),
                RelativeToAbsoluteCoordinates(Math.Max(ActualWidth, Width), Math.Max(ActualHeight, Height)),
                RelativeToAbsoluteCoordinates(Math.Max(ActualWidth,Width), 0) };
        }

        public virtual void Highlight()
        {
            //needs to be implemented by subclasses
        }

        public void Move(double dx, double dy)
        {
            Move(dx, dy, 0);
        }

        public virtual void Move(double dx, double dy, double rotationAngle)
        {
            rotate.Angle += rotationAngle;
            trans.X += dx;
            trans.Y += dy;
        }

        public virtual void Resize(ref double dx, ref double dy)
        {
            throw new NotImplementedException("Resize must be overriden by subclass!");
        }

        public void Rotate(double rotationAngle)
        {
            Move(0, 0, rotationAngle);
        }

        public virtual void Select()
        {
            //needs to be implemented by subclasses
        }

        #endregion

        #region Event Handling

        public void ForwardMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            //check if the event refers to this card
            //needs to be made in AgilePlannerMainWindow
            OnMouseLeftButtonUp(e);
        }

        public void ForwardMouseMove(MouseEventArgs e)
        {
            //check if the event refers to this card
            //needs to be made in AgilePlannerMainWindow
            OnMouseMove(e);

        }

        #endregion

        #endregion

        #region MultiTouch Event

        protected Point getPosition(Point p, UIElement element)
        {
            element.InputHitTest(p);
            
            //if (this.app.GetType() == typeof(AgilePlannerMicrosoftSurfaceWindow))
            //{
            //    return ((AgilePlannerMicrosoftSurfaceWindow)(this.app)).getCanvas().TranslatePoint(p, element);
            //}
            return (this.app).getCanvas().TranslatePoint(p, element);
        }

        public virtual void OnTouchDown(Point p)
        {
        }

        public virtual void OnTouchUp(Point p)
        {
        }

        public virtual void OnTouchMove(Point p)
        {
        }

        #endregion

        #region Custom Event Handling

        protected void fadingStoryBoard_Completed(object sender, EventArgs e)
        {
            //make the changes of the animations permanent
            if (fadingStoryBoard.Children.Count > 0)
            {
                double? sx = ((DoubleAnimation)fadingStoryBoard.Children[0]).To;
                double? sy = ((DoubleAnimation)fadingStoryBoard.Children[1]).To;

                double? op = null;

                if (fadingStoryBoard.Children.Count == 3)
                    op = ((DoubleAnimation)fadingStoryBoard.Children[2]).To;

                scale.ScaleX = (sx == null ? 1 : (double)sx);
                scale.ScaleY = (sy == null ? 1 : (double)sy);
                Opacity = (op == null ? 1 : (double)op);

                //clear all animations to save resources
                fadingStoryBoard.Children.Clear();
            }

            if (isDeleted)
                if (OnFadeOutCompleted != null)
                    OnFadeOutCompleted(this);
        }

        protected void DisplayObjectImpl_Loaded(object sender, RoutedEventArgs e)
        {
            FadeIn();
        }

        public void IndexCardDropped(DisplayObject sender, IndexCardDropEventArgs e)
        {
            if (OnIndexCardDropped != null)
                OnIndexCardDropped(sender, e);
        }

        #endregion

        #region Helper Functions


        protected void Fading(double from, double to, bool scaleOnly)
        {
            DoubleAnimation scaleXAnimation = new DoubleAnimation(from, to, TimeSpan.FromMilliseconds(CardConstants.FADING_DURATION));
            DoubleAnimation scaleYAnimation = new DoubleAnimation(from, to, TimeSpan.FromMilliseconds(CardConstants.FADING_DURATION));

            if (from == 0)
                to = 1;
            DoubleAnimation opacityAnimation = new DoubleAnimation(from, to, TimeSpan.FromMilliseconds(CardConstants.FADING_DURATION));

            //you can't access a property after it was animated
            //unless you set the FillBehavior to Stop
            scaleXAnimation.FillBehavior = FillBehavior.Stop;
            scaleYAnimation.FillBehavior = FillBehavior.Stop;
            opacityAnimation.FillBehavior = FillBehavior.Stop;

            //scale with the leftop point
            scale.CenterX = 0;// center.X;
            scale.CenterY = 0;// center.Y;

            fadingStoryBoard.Children.Add(scaleXAnimation);
            fadingStoryBoard.Children.Add(scaleYAnimation);

            if (!scaleOnly)
                fadingStoryBoard.Children.Add(opacityAnimation);

            Storyboard.SetTargetName(scaleXAnimation, "AnimatedScaleTransform");
            Storyboard.SetTargetProperty(scaleXAnimation,
                new PropertyPath(ScaleTransform.ScaleXProperty));

            Storyboard.SetTargetName(scaleYAnimation, "AnimatedScaleTransform");
            Storyboard.SetTargetProperty(scaleYAnimation,
                new PropertyPath(ScaleTransform.ScaleYProperty));

            Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath(StoryCardDisplayObjectImpl.OpacityProperty));

            fadingStoryBoard.Begin(this, true);

        }

        #region RNT

        public bool ApplyRNT(double x, double y)
        {
            return ApplyRNT(new Point(x, y));
        }

        public bool ApplyRNT(Point actual)
        {

            if (IsTranslated)
            {
                //just translate the card without rotation
                Move(actual.X - previous.X, actual.Y - previous.Y);
            }
            else if (!IsRNTEnabled)
            {
                //just translate the card without rotation
                Move(actual.X - previous.X, actual.Y - previous.Y);
            }
            else
            {
                Point vStart = new Point(previous.X - CenterAbsolute.X, previous.Y - CenterAbsolute.Y);
                double vStartLength = Util.GetDistance2D(CenterAbsolute, previous);

                Point vEnd = new Point(actual.X - CenterAbsolute.X, actual.Y - CenterAbsolute.Y);
                double vEndLength = Util.GetVector2DLength(vEnd);

                //angle is in radians!
                double angle = Util.GetSignedAngleBetweenVectors2D(vStart, vEnd);

                double currentAngle = Util.ToRadians(rotate.Angle);

                //angles are calculated modulo 360 degrees (or 2*PI if in radians)
                angle = angle % (2 * Math.PI);

                double s = (vEndLength - vStartLength) / vEndLength;

                Point vDisplacement = new Point(vEnd.X * s, vEnd.Y * s);

                Move(vDisplacement.X, vDisplacement.Y, Util.ToDegrees(angle));
            }

            //check if the center of the card remains within the boundaries of the
            //windows, if not make it do so
            if (IsPointOutsideWindowBorders(CenterAbsolute))
            {

                Point displacement = GetPointOutsideWindowBorderDisplacement(CenterAbsolute);

                Move(displacement.X, displacement.Y);
                return true;
            }

            //card didn't leave the window
            return false;

        }

        #endregion

        #region Tossing

        protected void ApplyTranslateOnlyTossing()
        {
            //clear the list of keyframes or you'll get unexpected
            //results
            translateXAnimation.KeyFrames.Clear();
            translateYAnimation.KeyFrames.Clear();
            rotateAnimation.KeyFrames.Clear();

            //friction to eventually make the tossing stop
            double modifier = (1 - CardConstants.CURRENT_FRICTION);

            //for the animation we need to keep a local copy
            //of the card center point that we can move
            Point localCenter = new Point(CenterAbsolute.X, CenterAbsolute.Y);

            //movement is relative to the last position
            Point previousPoint = new Point(trans.X, trans.Y);

            double stoppingDistance = 0;

            //as long as the tossing isnt aborted or the movement is unnoticeably small
            while (Util.GetVector2DLength(tossingVector) >= CardConstants.TOSSING_CUTOFF && IsTossed)
            {

                //slow down the movement
                tossingVector.X = tossingVector.X * modifier;
                tossingVector.Y = tossingVector.Y * modifier;

                Point actualPoint = new Point(
                    previousPoint.X + tossingVector.X,
                    previousPoint.Y + tossingVector.Y);

                translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(actualPoint.X));
                translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(actualPoint.Y));
                stoppingDistance += Util.GetDistance2D(previousPoint, actualPoint);

                previousPoint = actualPoint;

                //don't forget to move the card center
                localCenter.X += tossingVector.X;
                localCenter.Y += tossingVector.Y;

                //check if the card remains within window bounds
                if (IsPointOutsideWindowBorders(localCenter) && IsTossed)
                {
                    //  localCenter.X -= tossingVector.X;
                    //  localCenter.Y -= tossingVector.Y;
                    ResetInvalidTossingMove(localCenter);

                    //       IsTossed = false;
                }
            }

            IsTossed = false;

            double animationTime = CalculateAnimationTime(stoppingDistance); //* (stoppingDistance / stoppingDistanceWithoutBorders);


            translateXAnimation.Duration = TimeSpan.FromSeconds(animationTime);
            translateYAnimation.Duration = TimeSpan.FromSeconds(animationTime);


            if (stoppingDistance > 0)
                animationStoryBoard.Begin(this, true);
        }

        protected void ApplyRNTTossing()
        {
            //clear the list of keyframes or you'll get unexpected
            //results
            translateXAnimation.KeyFrames.Clear();
            translateYAnimation.KeyFrames.Clear();
            rotateAnimation.KeyFrames.Clear();

            //friction to eventually make the tossing stop
            double modifier = (1 - CardConstants.CURRENT_FRICTION);

            //for the animation we need to keep a local copy
            //of the card center point that we can move
            Point localCenter = new Point(CenterAbsolute.X, CenterAbsolute.Y);
            double localRotationAngle = rotate.Angle;

            //movement is relative to the last position
            Point previousPoint = new Point(CenterAbsolute.X, CenterAbsolute.Y);

            double stoppingDistance = 0;
            //double stoppingDistanceWithoutBorders = 0;


            //as long as the tossing isnt aborted or the movement is unnoticeably small
            while ((Util.GetVector2DLength(tossingVector) >= CardConstants.TOSSING_CUTOFF) && IsTossed)
            {

                //slow down the movement
                tossingVector.X = tossingVector.X * modifier;
                tossingVector.Y = tossingVector.Y * modifier;

                double angleDiff = Util.ToRadians(localRotationAngle - initAngle);
                double sine = Math.Sin(angleDiff);
                double cosine = Math.Cos(angleDiff);

                double rx = (initVector.X * cosine - initVector.Y * sine);
                double ry = (initVector.X * sine + initVector.Y * cosine);

                previousPoint.X = localCenter.X + rx;
                previousPoint.Y = localCenter.Y + ry;

                CalculateRNTSteps(ref localCenter, ref previousPoint);

                stoppingDistance += Util.GetVector2DLength(tossingVector);

                //stoppingDistanceWithoutBorders += Util.GetDistance2D(previousPoint, actualPoint);

                //don't forget to move the card center
                localCenter.X += tossingVector.X;
                localCenter.Y += tossingVector.Y;

                //check if the card remains within window bounds
                if (IsPointOutsideWindowBorders(localCenter) && IsTossed)
                {

                    ResetInvalidTossingMove(localCenter);


                    IsTossed = false;
                }
            }

            IsTossed = false;

            //if the card stops because it hits a window border the time is
            //not calculated properly and the movement might appear unnaturaly slow
            double animationTime = CalculateAnimationTime(stoppingDistance);

            translateXAnimation.Duration = TimeSpan.FromSeconds(animationTime);
            translateYAnimation.Duration = TimeSpan.FromSeconds(animationTime);
            rotateAnimation.Duration = TimeSpan.FromSeconds(animationTime);

            if (stoppingDistance > 0)
                animationStoryBoard.Begin(this, true);
            //    animationStoryBoard.b

        }

        protected Point ResetInvalidTossingMove(Point localCenter)
        {
            Point displacement = GetPointOutsideWindowBorderDisplacement(localCenter);

            int last = translateXAnimation.KeyFrames.Count - 1;

            //    double lastValidX = translateXAnimation.KeyFrames[last].Value += displacement.X;
            //    double lastValidY = translateYAnimation.KeyFrames[last].Value += displacement.Y;

            translateXAnimation.KeyFrames[last].Value += displacement.X;
            translateYAnimation.KeyFrames[last].Value += displacement.Y;
            //      translateXAnimation.KeyFrames.Remove(translateXAnimation.KeyFrames[last]);
            //     translateYAnimation.KeyFrames.Remove(translateYAnimation.KeyFrames[last]);

            //      translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(lastValidX));
            //      translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(lastValidY));

            localCenter.X += displacement.X;
            localCenter.Y += displacement.Y;
            return localCenter;
        }

        protected void CalculateRNTSteps(ref Point localCenter, ref Point previousPoint)
        {
            Point vStart = new Point(previousPoint.X - localCenter.X, previousPoint.Y - localCenter.Y);
            double vStartLength = Util.GetDistance2D(localCenter, previousPoint);

            Point vEnd = new Point(previousPoint.X + tossingVector.X - localCenter.X,
                previousPoint.Y + tossingVector.Y - localCenter.Y);
            double vEndLength = Util.GetVector2DLength(vEnd);

            //angle is in radians!
            double angle = Util.GetSignedAngleBetweenVectors2D(vStart, vEnd);

            double currentAngle = Util.ToRadians(rotate.Angle);

            //angles are calculated modulo 360 degrees (or 2*PI if in radians)
            angle = angle % (2 * Math.PI);

            double s = (vEndLength - vStartLength) / vEndLength;

            Point vDisplacement = new Point(vEnd.X * s, vEnd.Y * s);

            //Move(vDisplacement.X, vDisplacement.Y, Util.ToDegrees(angle));

            if (translateXAnimation.KeyFrames.Count == 0)
            {
                translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(trans.X + vDisplacement.X));
                translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(trans.Y + vDisplacement.Y));
                rotateAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(rotate.Angle + Util.ToDegrees(angle)));
            }
            else
            {
                int last = translateXAnimation.KeyFrames.Count - 1;
                translateXAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(translateXAnimation.KeyFrames[last].Value + vDisplacement.X));
                translateYAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(translateYAnimation.KeyFrames[last].Value + vDisplacement.Y));
                rotateAnimation.KeyFrames.Add(new LinearDoubleKeyFrame(rotateAnimation.KeyFrames[last].Value + Util.ToDegrees(angle)));
            }
        }

        protected double CalculateAnimationTime(double stoppingDistance)
        {
            //calculate the duration of the animation based on the speed of the
            //tossing gesture
            //TODO fix speed problem with tossing that gets stopped at the borders of the table
            double tps = TimeSpan.TicksPerSecond;
            double ticks = (recentTime[recentTime.Count - 1] - recentTime[0]).Ticks;
            double time = ticks * 10 / tps;
            double s0 = Util.GetDistance2D(recentPoints[0], recentPoints[recentPoints.Count - 1]);

            //I know that this looks weird but I always wanted to have a piece of
            //code that uses an inline-if-statement :)
            //it just prohibits a divide-by-zero error
            double v0 = s0 / (time != 0 ? time : 1);

            return (stoppingDistance * CardConstants.DECELERATION_CONSTANT / v0) * 2 * CardConstants.CURRENT_FRICTION;
        }

        #endregion


        protected void AddMousePoint(Point actual)
        {
            //add the current mouse position to the list
            //of recorded coordinates for tossing
            if (recentPoints.Count <= CardConstants.NUM_RECENT_POINTS)
            {
                recentPoints.Add(actual);
            }
            else
            {
                recentPoints.Remove(recentPoints[0]);
                recentPoints.Add(actual);
            }
        }

        protected void AddTimePoint(DateTime current)
        {
            //add the current time to the list
            //of recorded coordinates for tossing
            if (recentTime.Count <= CardConstants.NUM_RECENT_POINTS)
            {
                recentTime.Add(current);
            }
            else
            {
                recentTime.Remove(recentTime[0]);
                recentTime.Add(current);
            }
        }

        public Point RelativeToAbsoluteCoordinates(double relX, double relY)
        {
            return RelativeToAbsoluteCoordinates(new Point(relX, relY));
        }

        public Point RelativeToAbsoluteCoordinates(Point relPoint)
        {
            return Util.RelativeToAbsoluteCoordinates(relPoint, center, rotate.Angle, trans.X, trans.Y);
        }

        protected bool IsPointOutsideWindowBorders(Point p)
        {
            double windowWidth = app.GetWidth();
            double windowHeight = app.GetHeight();
            double mockupWidth = this.Width * Properties.Settings.Default.Scale;
            double mockupHeight = this.Height * Properties.Settings.Default.Scale;
            if (this is StoryCardDisplayObjectImpl)
            {
                double diagonal = Math.Sqrt(Math.Pow(mockupWidth, 2) + Math.Pow(mockupHeight, 2));
                if (p.X > diagonal / 2 && p.Y > diagonal / 2 && p.X < windowWidth - diagonal / 2 && p.Y < windowHeight - diagonal / 2)
                    return false;
                else
                {
                    double currentAngle = Util.ToRadians(Math.Abs(this.rotate.Angle) % 360);
                    double diagonalAngle = Math.Atan(mockupHeight / mockupWidth);//this is between -2/pi and 2/pi
                    if (currentAngle >= 0 && currentAngle <= Math.PI / 2)
                    {
                        double minimizeHeight = Math.Sin(currentAngle + diagonalAngle) * diagonal / 2;
                        double minimizeWidth = Math.Cos(currentAngle - diagonalAngle) * diagonal / 2;
                        return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                    }
                    else if (currentAngle > Math.PI / 2 && currentAngle <= Math.PI)
                    {
                        double minimizeHeight = Math.Cos(currentAngle - Math.PI / 2 - diagonalAngle) * diagonal / 2;
                        double minimizeWidth = Math.Abs(Math.Cos(Math.PI - currentAngle - diagonalAngle)) * diagonal / 2;
                        return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                    }
                    else if (currentAngle > Math.PI && currentAngle <= 1.5 * Math.PI)
                    {
                        double minimizeHeight = Math.Abs(Math.Cos(diagonalAngle - 1.5 * Math.PI + currentAngle)) * diagonal / 2;
                        double minimizeWidth = Math.Abs(Math.Cos(currentAngle - Math.PI - diagonalAngle)) * diagonal / 2;
                    }
                    else
                    {
                        double minimizeHeight = Math.Abs(Math.Sin(2 * Math.PI - currentAngle + diagonalAngle)) * diagonal / 2;
                        double minimizeWidth = Math.Abs(Math.Cos(2 * Math.PI - currentAngle - diagonalAngle)) * diagonal / 2;
                        return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                    }
                    return true;
                }

            }
            else if (this is IterationDisplayObjectImpl)
            {
                return (p.X < mockupWidth / 2 || p.Y < mockupHeight / 2 || p.X > windowWidth - mockupWidth / 2 || p.Y > windowHeight - mockupHeight / 2);
            }
            else
                return (p.X < 0 || p.X > windowWidth || p.Y < 0 || p.Y > windowHeight);
        }

        protected Point GetPointOutsideWindowBorderDisplacement(Point p)
        {
            double windowWidth = app.GetWidth();
            double windowHeight = app.GetHeight();

            double rx = 0;
            double ry = 0;
            if (this is StoryCardDisplayObjectImpl)
            {
                double diagonal = Math.Sqrt(Math.Pow(this.Width * Properties.Settings.Default.Scale, 2) + Math.Pow(this.Height * Properties.Settings.Default.Scale, 2));
                double currentAngle = Util.ToRadians(Math.Abs(this.rotate.Angle) % 360);
                double minimizeHeight = 0, minimizeWidth = 0;
                double diagonalAngle = Math.Atan(this.Height / this.Width);//this is between -2/pi and 2/pi
                if (currentAngle >= 0 && currentAngle <= Math.PI / 2)
                {
                    minimizeHeight = Math.Sin(currentAngle + diagonalAngle) * diagonal / 2;
                    minimizeWidth = Math.Cos(currentAngle - diagonalAngle) * diagonal / 2;
                    //    return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                }
                else if (currentAngle > Math.PI / 2 && currentAngle <= Math.PI)
                {
                    minimizeHeight = Math.Cos(currentAngle - Math.PI / 2 - diagonalAngle) * diagonal / 2;
                    minimizeWidth = Math.Cos(Math.PI - currentAngle - diagonalAngle) * diagonal / 2;
                    //   return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                }
                else if (currentAngle > Math.PI && currentAngle <= 1.5 * Math.PI)
                {
                    minimizeHeight = Math.Abs(Math.Cos(diagonalAngle - 1.5 * Math.PI + currentAngle)) * diagonal / 2;
                    minimizeWidth = Math.Abs(Math.Cos(currentAngle - Math.PI - diagonalAngle)) * diagonal / 2;
                }
                else
                {
                    minimizeHeight = Math.Abs(Math.Sin(2 * Math.PI - currentAngle + diagonalAngle)) * diagonal / 2;
                    minimizeWidth = Math.Abs(Math.Cos(2 * Math.PI - currentAngle - diagonalAngle)) * diagonal / 2;
                    //    return (p.X < minimizeWidth || p.Y < minimizeHeight || p.X > windowWidth - minimizeWidth || p.Y > windowHeight - minimizeHeight);
                }
                if (p.X < minimizeWidth)
                    rx = minimizeWidth - p.X;
                if (p.X > windowWidth - minimizeWidth)
                    rx = windowWidth - minimizeWidth - p.X;

                if (p.Y < minimizeHeight)
                    ry = minimizeHeight - p.Y;
                if (p.Y > windowHeight - minimizeHeight)
                    ry = windowHeight - minimizeHeight - p.Y;
            }
            else if (this is IterationDisplayObjectImpl
                || this is StateAreaDisplayObjectImpl)
            {
                if (p.X < this.Width / 2)
                    rx = this.Width / 2 - p.X;
                if (p.X > windowWidth - this.Width / 2)
                    rx = windowWidth - this.Width / 2 - p.X;

                if (p.Y < this.Height / 2)
                    ry = this.Height / 2 - p.Y;
                if (p.Y > windowHeight - this.Height / 2)
                    ry = windowHeight - this.Height / 2 - p.Y;
            }
            else
            {
                if (p.X < 0)
                    rx = -p.X;
                if (p.X > windowWidth)
                    rx = windowWidth - p.X;
                if (p.Y < 0)
                    ry = -p.Y;
                if (p.Y > windowHeight)
                    ry = windowHeight - p.Y;
            }


            return new Point(rx, ry);
        }

        #endregion

        #region Initialization

        protected void InitializeTransformations()
        {
            //      this.RenderTransformOrigin = new Point(0.5, 0.5);
            group = new TransformGroup();
            rotate = new RotateTransform(0);
            scale = new ScaleTransform(1, 1);
            skew = new SkewTransform(0, 0);
            trans = new TranslateTransform(0, 0);

            //add the transformations to the TransformGroup
            group.Children.Add(rotate);
            group.Children.Add(scale);
            group.Children.Add(skew);

            //for whatever reason: keep trans as the last transformation
            //or the whole thing blows up!
            group.Children.Add(trans);

            //now all transformations are connected
            //to this card
            RenderTransform = group;
            //    this.RenderTransformOrigin = new Point(0.5, 0.5);
        }

        /// <summary>
        /// Initializes the animations.
        /// </summary>
        protected void InitializeAnimations()
        {
            NameScope.SetNameScope(this, new NameScope());

            animationStoryBoard = new Storyboard();
            fadingStoryBoard = new Storyboard();
            fadingStoryBoard.Completed += new EventHandler(fadingStoryBoard_Completed);
            animationStoryBoard.FillBehavior = FillBehavior.Stop;
            //its easier to access the translation if you
            //give it a name
            this.RegisterName("AnimatedTranslateTransform", trans);
            this.RegisterName("AnimatedRotateTransform", rotate);
            this.RegisterName("AnimatedScaleTransform", scale);

            translateXAnimation = new DoubleAnimationUsingKeyFrames();
            translateYAnimation = new DoubleAnimationUsingKeyFrames();
            rotateAnimation = new DoubleAnimationUsingKeyFrames();



            //      animationStoryBoard.RepeatBehavior = 1;

            //link the animation with the property it should change (here the X and Y values
            //of the translation and the rotation angle of the card)
            Storyboard.SetTargetName(translateXAnimation, "AnimatedTranslateTransform");
            Storyboard.SetTargetProperty(translateXAnimation,
                new PropertyPath(TranslateTransform.XProperty));


            Storyboard.SetTargetName(translateYAnimation, "AnimatedTranslateTransform");
            Storyboard.SetTargetProperty(translateYAnimation,
                new PropertyPath(TranslateTransform.YProperty));

            Storyboard.SetTargetName(rotateAnimation, "AnimatedRotateTransform");
            Storyboard.SetTargetProperty(rotateAnimation,
                new PropertyPath(RotateTransform.AngleProperty));

            //don't forget to add the animations to the storyboard or nothin
            //will happen
            animationStoryBoard.Children.Add(translateXAnimation);
            animationStoryBoard.Children.Add(translateYAnimation);
            animationStoryBoard.Children.Add(rotateAnimation);

            animationStoryBoard.Begin(this, true);
            animationStoryBoard.Stop(this);
        }

        public Storyboard getanimationStoryBoard()
        {
            return this.animationStoryBoard;
        }

        public void initializeDisplayObjectByScale(double scale)
        {
            //  this.Resize();
        }
        #endregion

        #region Automation

        /// <summary>
        /// Provides an implementation of this control as per the WPF structure.
        /// </summary>
        /// <returns>Returns a new AutomationPeer for this type.</returns>
        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new DisplayObjectImplAutomationPeer(this);
        }

        public class DisplayObjectImplAutomationPeer : FrameworkElementAutomationPeer, ITransformProvider
        {
            DisplayObjectImpl host;

            /// <summary>
            /// Inits a new instance from the base class.
            /// </summary>
            /// <param name="control">The calling control.</param>
            public DisplayObjectImplAutomationPeer(DisplayObjectImpl control)
                : base(control)
            {
                host = control;
            }

            /// <summary>
            /// This is the name of the UIElement associated with the AutomationPeer.
            /// </summary>
            /// <returns>The name of this control type.</returns>
            protected override string GetClassNameCore()
            {
                return "DisplayObjectImpl";
            }

            /// <summary>
            /// Sets the AutomationID.
            /// </summary>
            /// <returns>The name of this display object.</returns>
            protected override string GetAutomationIdCore()
            {
                return "DisplayObject:" + host.ID;
            }

            /// <summary>
            /// Gets the control type of the UIElement associated with this AutomationPeer.
            /// </summary>
            /// <returns>The custom enumeration value.</returns>
            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Custom;
            }

            /// <summary>
            /// Gets the control pattern for this UIElement.
            /// </summary>
            /// <param name="patternInterface">The type of AutomationElement to try for.</param>
            /// <returns>An object of the given type, or null.</returns>
            public override object GetPattern(PatternInterface patternInterface)
            {
                return (patternInterface == PatternInterface.Transform
                    ? this
                    : base.GetPattern(patternInterface));
            }

            public virtual void Move(double x, double y)
            {
                if (CanMove)
                    host.Move(x, y);
            }

            public virtual void Resize(double width, double height)
            {
                if (CanResize)
                    host.Resize(ref width, ref height);
            }

            public virtual void Rotate(double degrees)
            {
                if (CanRotate)
                    host.Rotate(degrees);
            }

            public bool CanMove
            {
                get
                {
                    return !host.isPinned;
                }
            }

            /// <summary>
            /// Determines whether the display object can be resized.  Will need to 
            /// be overriden in StoryCardDOI.
            /// </summary>
            public virtual bool CanResize
            {
                get
                {
                    return false;
                }
            }

            public bool CanRotate
            {
                get
                {
                    return !host.isPinned;
                }
            }
        }

        #endregion
        #region resizecard

        public void refitCardCenterAndAbsolutePosition(double dx, double dy)
        {
          
                this.rotate.CenterX = this.Width / 2;
                this.rotate.CenterY = this.Height / 2;
                this.trans.X += Properties.Settings.Default.Scale * 0.5 * (dx * Math.Cos(Util.ToRadians(this.RotationAngle)) - dy * Math.Sin(Util.ToRadians(this.RotationAngle)) - dx);
                this.trans.Y += Properties.Settings.Default.Scale * 0.5 * (dx * Math.Sin(Util.ToRadians(this.RotationAngle)) + dy * Math.Cos(Util.ToRadians(this.RotationAngle)) - dy);
               
           
        }
        #endregion
    }
}
